1   /*
2    * Copyright (C) 2013 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.hash;
16  
17  import static com.google.common.base.Preconditions.checkNotNull;
18  
19  import com.google.common.annotations.Beta;
20  
21  import java.io.FilterInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  /**
26   * An {@link InputStream} that maintains a hash of the data read from it.
27   *
28   * @author Qian Huang
29   * @since 16.0
30   */
31  @Beta
32  public final class HashingInputStream extends FilterInputStream {
33    private final Hasher hasher;
34  
35    /**
36     * Creates an input stream that hashes using the given {@link HashFunction} and delegates all data
37     * read from it to the underlying {@link InputStream}.
38     *
39     * <p>The {@link InputStream} should not be read from before or after the hand-off.
40     */
41    public HashingInputStream(HashFunction hashFunction, InputStream in) {
42      super(checkNotNull(in));
43      this.hasher = checkNotNull(hashFunction.newHasher());
44    }
45  
46    /**
47     * Reads the next byte of data from the underlying input stream and updates the hasher with
48     * the byte read.
49     */
50    @Override
51    public int read() throws IOException {
52      int b = in.read();
53      if (b != -1) {
54        hasher.putByte((byte) b);
55      }
56      return b;
57    }
58  
59    /**
60     * Reads the specified bytes of data from the underlying input stream and updates the hasher with
61     * the bytes read.
62     */
63    @Override
64    public int read(byte[] bytes, int off, int len) throws IOException {
65      int numOfBytesRead = in.read(bytes, off, len);
66      if (numOfBytesRead != -1) {
67        hasher.putBytes(bytes, off, numOfBytesRead);
68      }
69      return numOfBytesRead;
70    }
71  
72    /**
73     * mark() is not supported for HashingInputStream
74     * @return {@code false} always
75     */
76    @Override
77    public boolean markSupported() {
78      return false;
79    }
80  
81    /**
82     * mark() is not supported for HashingInputStream
83     */
84    @Override
85    public void mark(int readlimit) {}
86  
87    /**
88     * reset() is not supported for HashingInputStream.
89     * @throws IOException this operation is not supported
90     */
91    @Override
92    public void reset() throws IOException {
93      throw new IOException("reset not supported");
94    }
95  
96    /**
97     * Returns the {@link HashCode} based on the data read from this stream. The result is
98     * unspecified if this method is called more than once on the same instance.
99     */
100   public HashCode hash() {
101     return hasher.hash();
102   }
103 }